package com.zonski.jbots.desktop.client;

import java.awt.event.*;
import java.awt.*;
import java.awt.image.VolatileImage;
import java.util.HashMap;

import com.zonski.jbots.engine.*;
import com.zonski.jbots.engine.entity.AbstractMind;
import com.zonski.jbots.game.JBotsGameController;
import com.zonski.jbots.game.JBotsConstants;
import com.zonski.jbots.game.resources.EggMaker;
import com.zonski.jbots.desktop.client.entity.LayerImageFactory;
import com.zonski.jbots.desktop.client.entity.HashedEntityRendererFactory;
import com.zonski.jbots.desktop.client.entity.ImageEntityRenderer;
import com.zonski.jbots.desktop.client.recolor.GreyRecolorer;

public class PainterCanvas extends Canvas 
{
    private static final int PLAYER_IMAGE_DIMENSION = 32;
    private static final int MAX_HEALTH_LENGTH = 64;
    private static final int MAX_MAX_CHARGE = JBotsConstants.MAX_CHARGE;

    private static final Color NRG_COLOR = new Color(128, 200, 255);

    private VolatileImage bufferImage;

    private JBotsGameController controller;
    private RoomRenderer roomRenderer;

    private int offsetX;
    private int offsetY;

    private HashMap playerImages = new HashMap(5);
    private Image leftDeadImage;
    private Image rightDeadImage;

    public PainterCanvas(JBotsGameController controller)
    {
        this.addComponentListener(new CanvasComponentListener());
        this.addMouseListener(new CanvasMouseListener());
        this.controller = controller;
    }

    public void setPlayerImage(Image image)
    {
        this.playerImages.put(new Integer(0), image);
        this.playerImages.put(new Integer(1), ImageUtilities.recolorImage(image, this, LayerImageFactory.RECOLORER[2]));

        this.playerImages.put(new Integer(2),
                ImageUtilities.flipImageVerticalAxis(
                        ImageUtilities.recolorImage(image, this, LayerImageFactory.RECOLORER[3]), this)
        );
        this.playerImages.put(new Integer(3),
                ImageUtilities.flipImageVerticalAxis(
                    ImageUtilities.recolorImage(image, this, LayerImageFactory.RECOLORER[4]), this)
        );

        this.leftDeadImage = ImageUtilities.recolorImage(image, this, GreyRecolorer.INSTANCE);
        this.rightDeadImage = ImageUtilities.flipImageVerticalAxis(leftDeadImage, this);
    }

    public int getOffsetX()
    {
        return this.offsetX;
    }

    public int getOffsetY()
    {
        return this.offsetY;
    }

    public void setOffset(int offsetX, int offsetY)
    {
        this.offsetX = offsetX;
        this.offsetY = offsetY;
    }

    public RoomRenderer getRoomRenderer()
    {
        return this.roomRenderer;
    }

    public void setRoomRenderer(RoomRenderer roomRenderer)
    {
        this.roomRenderer = roomRenderer;
    }

    public void recreateBuffer()
    {
        // create the buffer at the component size
        this.bufferImage = this.createVolatileImage(getWidth(), getHeight());
    }

    public void draw()
    {
        do
        {
            if(this.bufferImage == null || this.bufferImage.validate(getGraphicsConfiguration()) == VolatileImage.IMAGE_INCOMPATIBLE)
            {
                recreateBuffer();
            }
            Graphics g = this.bufferImage.createGraphics();
            draw(g);
            g.dispose();
        }while(this.bufferImage.contentsLost());
    }

    public void draw(Graphics g)
    {
        g.setColor(Color.darkGray);
        g.fillRect(0, 0, getWidth(), getHeight());
        switch(controller.getMode())
        {
            case JBotsGameController.DEMO:
            case JBotsGameController.GAME:
            case JBotsGameController.GAME_OVER:
            case JBotsGameController.PRE_LEVEL:
                if(this.roomRenderer != null)
                {
                    drawPlayers(g);

                    Room room = this.controller.getEngine().getRoom();

                    // draw the rooms name - assume that the graphics is offset somewhat
                    String roomName = room.getName();
                    if(roomName != null)
                    {
                        g.setColor(Color.lightGray);
                        g.setFont(new Font("Dialog", Font.PLAIN, 12));
                        FontMetrics fm = g.getFontMetrics();
                        g.drawString(roomName, (this.getWidth() - fm.stringWidth(roomName))/2, fm.getHeight());
                    }

                    int width = room.getWidth();
                    int height = room.getHeight();
                    g.translate((getWidth() - width)/2 + offsetX,
                            (getHeight() - height)/2 + offsetY);
                    roomRenderer.render(g, room);
                    String text = null;
                    if(controller.getMode() == JBotsGameController.DEMO)
                    {
                        text = "DEMO";
                    }else if(controller.getMode() == JBotsGameController.GAME_OVER){
                        text = "GAME OVER";
                    }else if(controller.getMode() == JBotsGameController.PRE_LEVEL){
                        text = "Level "+Integer.toString(controller.getLevel()+1);
                    }
                    if(text != null)
                    {
                        g.setColor(Color.WHITE);
                        g.setFont(new Font("SansSerif", Font.BOLD, 20));
                        FontMetrics fm = g.getFontMetrics();
                        g.drawString(text, (width - fm.stringWidth(text))/2,
                                (height + fm.getHeight())/2);
                    }
                }
                break;
            case JBotsGameController.LOADING:
            case JBotsGameController.GAME_START:
                {
                    g.setColor(Color.WHITE);
                    g.setFont(new Font("SansSerif", Font.BOLD, 20));
                    FontMetrics fm = g.getFontMetrics();
                    String loading = "Loading...";
                    g.drawString(loading, (this.getWidth() - fm.stringWidth(loading))/2,
                            (this.getHeight() + fm.getHeight())/2);
                }
                break;
            case JBotsGameController.INTRO:
                {
                    // TODO : display the introduction screen
                    g.setColor(Color.WHITE);
                    g.setFont(new Font("SansSerif", Font.BOLD, 20));
                    FontMetrics fm = g.getFontMetrics();
                    String text = "Click to Play";
                    g.drawString(text, (this.getWidth() - fm.stringWidth(text))/2,
                            (this.getHeight() + fm.getHeight())/2);
                }
                break;
        }
    }

    public void update(Graphics g)
    {
        draw();
        paint(g);
    }

    public void paint(Graphics g)
    {
        if(this.bufferImage == null)
        {
            recreateBuffer();
        }
        do
        {
            int returnCode = this.bufferImage.validate(getGraphicsConfiguration());
            if(returnCode == VolatileImage.IMAGE_RESTORED)
            {
                draw();
            }else if(returnCode == VolatileImage.IMAGE_INCOMPATIBLE){
                recreateBuffer();
                draw();
            }
            g.drawImage(this.bufferImage, 0, 0, this);
        }while(this.bufferImage.contentsLost());
    }

    private class CanvasComponentListener extends ComponentAdapter
    {
        public CanvasComponentListener()
        {
        }

        public void componentResized(ComponentEvent e)
        {
            recreateBuffer();
        }
    }

    private void drawPlayers(Graphics g)
    {
        Room room = this.controller.getEngine().getRoom();
        int width = room.getWidth();
        int height = room.getHeight();
        int d = PLAYER_IMAGE_DIMENSION;
        int gap = 12;

        drawPlayer(g, 0, (this.getWidth() - width)/2 - d - gap, (this.getHeight() - height)/2, true);
        drawPlayer(g, 1, (this.getWidth() - width)/2 - d - gap, this.getHeight()/2, true);
        drawPlayer(g, 2, (this.getWidth() + width)/2 + gap, this.getHeight()/2, false);
        drawPlayer(g, 3, (this.getWidth() + width)/2 + gap, (this.getHeight() - height)/2, false);
    }

    private final void drawPlayer(Graphics g, int id, int x, int y, boolean left)
    {
        AbstractMind mind = (AbstractMind)this.controller.getEngine().getMind(Engine.getPlayerMindName(id));
        Entity entity = mind.getEntity();
        Image image;
        if(entity == null || entity.isRemovable())
        {
            if(left)
            {
                image = this.leftDeadImage;
            }else{
                image = this.rightDeadImage;
            }
        }else{
            image = (Image)this.playerImages.get(new Integer(id));

            int charge = entity.getCharge();
            int maxCharge = entity.getMaxCharge();
            int maxHealthLength = (MAX_HEALTH_LENGTH * maxCharge) / MAX_MAX_CHARGE;
            int length = (charge * maxHealthLength)/maxCharge;

            int rx;
            if(!left)
            {
                rx = x - 8;
            }else{
                rx = x + PLAYER_IMAGE_DIMENSION + 4;
            }
            int ry = y;
            g.setColor(NRG_COLOR);
            g.fillRect(rx, ry + (MAX_HEALTH_LENGTH - length), 4, length);

            // check the egg makers for charge
            QuickVector resources = entity.getResources();
            EggMaker eggMaker = null;
            for(int i=0; i<resources.size(); i++)
            {
                Object resource = resources.elementAt(i);
                if(resource instanceof EggMaker)
                {
                    EggMaker em = (EggMaker)resource;
                    if(em.isBeingUsed())
                    {
                        if(eggMaker == null || em.getCharge() > eggMaker.getCharge())
                        {
                            eggMaker = em;
                        }
                    }
                }
            }
            if(eggMaker != null)
            {
                int minLength = (eggMaker.getMinCharge() * maxHealthLength)/ maxCharge;
                int maxLength = (eggMaker.getMaxCharge() * maxHealthLength)/ maxCharge;
                g.setColor(Color.yellow);
                g.fillRect(rx, ry + (MAX_HEALTH_LENGTH - minLength), 4, minLength);
                g.drawLine(rx, ry + (MAX_HEALTH_LENGTH - maxLength), rx+4, ry + (MAX_HEALTH_LENGTH - maxLength));

                int chargeLength = (eggMaker.getCharge() * maxHealthLength)/ maxCharge;
                g.setColor(Color.red);
                g.fillRect(rx, ry + (MAX_HEALTH_LENGTH - chargeLength), 4, chargeLength);

                String toCreate = eggMaker.getToCreate();
                if(toCreate != null)
                {
                    HashedEntityRendererFactory rendererFactory;
                    rendererFactory = (HashedEntityRendererFactory)this.roomRenderer.getEntityRendererFactory();
                    ImageEntityRenderer renderer = (ImageEntityRenderer)rendererFactory.getRenderer(toCreate);
                    LayerImageFactory imageFactory = (LayerImageFactory)renderer.getImageFactory(Modes.NONE);
                    Image creating = imageFactory.getImage(id);
                    int width = creating.getWidth(this);
                    int height = creating.getHeight(this);
                    int bx = x + (PLAYER_IMAGE_DIMENSION - width)/2;
                    int by = y+PLAYER_IMAGE_DIMENSION;
                    int cnr = 3;
                    g.drawImage(creating, bx, by, this);
                    g.setColor(Color.lightGray);
                    g.drawLine(bx - 1, by - 1, bx + cnr -1, by - 1);
                    g.drawLine(bx - 1, by - 1, bx - 1, by + cnr - 1);
                    g.drawLine(bx + width + 1, by - 1, bx + width - cnr + 1, by - 1);
                    g.drawLine(bx + width + 1, by - 1, bx + width + 1, by + cnr - 1);
                    g.drawLine(bx + width + 1, by + height + 1, bx + width - cnr + 1, by + height + 1);
                    g.drawLine(bx + width + 1, by + height + 1, bx + width + 1, by + height - cnr + 1);
                    g.drawLine(bx - 1, by + height + 1, bx + cnr -1, by + height + 1);
                    g.drawLine(bx - 1, by + height + 1, bx - 1, by + height - cnr + 1);
                }
            }

            g.setColor(Color.white);
            g.drawRect(rx, ry + MAX_HEALTH_LENGTH - maxHealthLength, 4, maxHealthLength);
        }
        g.drawImage(image, x, y, this);
    }

    private class CanvasMouseListener extends MouseAdapter
    {
        public void mouseClicked(MouseEvent event)
        {
            if(PainterCanvas.this.controller.getMode() != JBotsGameController.GAME &&
               PainterCanvas.this.controller.getMode() != JBotsGameController.PRE_LEVEL)
            {
                PainterCanvas.this.controller.setMode(JBotsGameController.GAME_START);
                repaint();
            }
        }
    }

}
